-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added ability to include recipes smartly #1765
base: master
Are you sure you want to change the base?
Conversation
@recipe_depedancies ||= Mash.new | ||
@recipe_depedancies.merge!(dependency) | ||
end | ||
def get(recipe) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/EmptyLineBetweenDefs: Use empty lines between method definitions. (https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you think of a way to test this feature? Replacing include_recipe
would introduce a potentially large regression 😄
Can you give some ballpark as to what performance impact this would have? Or maybe do you have examples where you measured the performance impact?
i did do some initial testing, here is the sample: sjamgade/crowbar-openstack@4fc8662 this is not going to replace |
What I was trying to get at was that by replacing an Besides testing whether a dependency is really not included when its attributes have not changed, we need to ask the following question: For maximizing performance gain, the list of attributes of a dependency needs to be minimal in a sense that it contains only necessary attributes. However, a missing attribute introduces a regression. How do we test that this set does not introduce a regression because it's missing something? |
thats one way to look at it. one shortcut I though of
I do no expect to see too many across barclamp dependencies and if there are, these recipes (and/or resources ) can be grouped a separate recipe |
5edbeda
to
319518b
Compare
prev_cfg = load(group, barclamp, instance) | ||
instance = infer_instance(group, barclamp, instance) | ||
cur_cfg = Chef::Role.load "#{barclamp}-config-#{instance}" | ||
return prev_cfg, cur_cfg.default_attributes[barclamp] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/RedundantReturn: Redundant return detected. To return multiple values, use an array. (https://github.com/bbatsov/ruby-style-guide#no-explicit-return)
Chef::Log.debug("[smart] due to change in: #{dependency}") | ||
include_recipe recipe | ||
return | ||
end # each |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/CommentedKeyword: Do not place comments on the same line as the end keyword.
e9f2fa9
to
e8a34bf
Compare
return hash | ||
end | ||
end | ||
Chef::Log.debug("[smart] hash does not respond to []") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this (and the variable name) is misleading. if variable doesn't respond to []
it's most probably not a hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about collection
?
since it is a collection of values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it could also be a single value. this is exactly when the log would trigger. I generally think that this message could be more informative (e.g. "%param name% is not a collection" or sth like this).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exactly, it could be a single value or a collection ( i could not think of a word for that)
and that is why the log says the collection did not respond to ...
because it is a int/string/....
Roles can get smart by making recipe inform the role about its dependencies across the proposal. So a recipe will be included by the role only if the attributes registered by the recipe are actually changed in current chef-client run. a recipe can register its dependencies as ``` mydependson = { "horizon::server" => [ ['glance','api','bind_port'] ] } BarclampLibrary::Barclamp::DependsOn.add(mydependson) ``` So here the "horizon::server" is informing about its dependencies. This is accomplished by comparing the value of the current attributes of node against the one already stored in the databag after the previous successful chef-client run. BarclampLibrary::Barclamp::DependsOn.add: takes in a map: recipe_name => [ [barclampname,drill,down,till,value], [otherbarclampname,onlyhere], ] This map is flushed and recreated only everyrun, however like resources this could also be cached. this behaviour can be altered by adding flag to config and using it 'include_recipe_smartly', however that is an extension to this behavior and can be addressed in subsequent commits Use the role(proposal) that was committed to compare against databag This object enables us to do real comparison on proposal level. Thus every barclamp can have proposal level dependency hound fixes and refactor
e8a34bf
to
51f94a3
Compare
collection = collection[item] | ||
end | ||
rescue NoMethodError | ||
Chef::Log.info("[smart] collection did not respond to []/key? while looking for #{item}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Metrics/LineLength: Line is too long. [101/100] (https://github.com/SUSE/style-guides/blob/master/Ruby.md#metricslinelength)
collection = collection[item] | ||
end | ||
rescue NoMethodError | ||
Chef::Log.info("[smart] collection did not respond to []/key? while looking for #{item}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Metrics/LineLength: Line is too long. [101/100] (https://github.com/SUSE/style-guides/blob/master/Ruby.md#metricslinelength)
Modified the example test to include proposal level depedancy : sjamgade/crowbar-openstack@954354e |
One key thing to be aware of is that not all barclamps save something to the data bags, and among the ones that do, not all of them simply copy the content of the proposal (eg: dns and ntp). So the proposed approach here only works for the openstack barclamps. Which is still not too bad, but still something to be aware of. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-openstack barclamps are relatively fast and I dont think we need to consider them with respect to this PR.
But I guess it means that if e.g. something (anything) changes in a barclamp that doesn't maintain the config databags in the way we expect it, we need to always run the full set of recipes, right? As far as I can see the current code doesn't really handle that case. Also keep in mind that even not all barclamps from crowbar-openstack are writing the config databags yet. At least database is not using it currently I think.
I haven't fully made up my mind on this change yet. On the one hand it introduces a bit of magic that can have weird side-effects (e.g. a local chef-client run will not revert up any manual config change that were done or crowbar_join might be missing stuff on boot up). And there is a risk of this change causing some "interesting" corner cases. OTOH, I guess this will reduce the time needed for a deployment and may be more importantly for later config changes so there is definitely a significant benefit.
In general I think the code could benefit from a few more comments here and there to make it easier for reviewers to understand what it actually does.
mydependson = {
"horizon::server" => [
["horizon"] # depend on the entire proposal
Does this part really work with the current code?
BTW, I didn't really check this, but are you sure that this PR also works when e.g. the chef-client run on a node fails during apply. (Is the databag only written after a successful run?)
you are right, the code does not handle this case (recipes dependent on stuff outside the databags), but such recipes can continue to use
local chef run can be asked to always run via a "config" option, which I will add if this PR sees the master branch.
yes I have been testing it. :-)
yes :-) |
I understand there is a lot of uncertainty about the behavior of almost all barclamps as everyone here is suggesting. But this PR is merely adding support for skipping unnecessary stuff and not actually affecting the real workflow. And as followup PR, I will add the option to disable skipping entirely. Is there anything else we can do to move things forward here. |
There is also some uncertainty (at least from me) about how much additional maintenance this requires. This finally adds another place where we carefully need to think and maintain dependencies between barclamps (at least of the barclamps for which we actually want to use it). We haven't been particularly good at that in the past IMO :)
I guess some more reviews/opinions form others might help. @aspiers maybe? |
most concerns I raised have been addressed
include_recipe recipe | ||
included = true | ||
break | ||
end.empty? and begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/AndOr: Use && instead of and. (https://github.com/bbatsov/ruby-style-guide#no-and-or-or)
# "horizon::server" => [ | ||
# ['glance','api','bind_port'], | ||
# ['horizon'] | ||
# ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
Layout/CommentIndentation: Incorrect indentation detected (column 2 instead of 8).
# if it has changed include the recipe | ||
# "horizon::server" => [ | ||
# ['glance','api','bind_port'], | ||
# ['horizon'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
# Iterate over the dependcies and find if either have changed | ||
# if it has changed include the recipe | ||
# "horizon::server" => [ | ||
# ['glance','api','bind_port'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
def include_recipe_smartly(*list_of_recipes) | ||
# Iterate over the dependcies and find if either have changed | ||
# if it has changed include the recipe | ||
# "horizon::server" => [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
end | ||
|
||
# a attrlist is ['api','bind_port'] (array) | ||
# and collection is assumed to be a hash like object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
return prev_cfg, cur_cfg.default_attributes[barclamp] | ||
end | ||
|
||
# a attrlist is ['api','bind_port'] (array) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
# this method will return collection['api']['bind_port'] | ||
# | ||
# - abort and return nil , if any key not found | ||
# - log accordingly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
Layout/CommentIndentation: Incorrect indentation detected (column 2 instead of 8).
# and collection is assumed to be a hash like object | ||
# this method will return collection['api']['bind_port'] | ||
# | ||
# - abort and return nil , if any key not found |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
# a attrlist is ['api','bind_port'] (array) | ||
# and collection is assumed to be a hash like object | ||
# this method will return collection['api']['bind_port'] | ||
# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/Tab: Tab detected. (https://github.com/bbatsov/ruby-style-guide#spaces-indentation)
Layout/TrailingWhitespace: Trailing whitespace detected. (https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace)
Roles can get smart by making recipe inform the role about its dependencies
across the proposal.
So a recipe will be included by the role only if the attributes
registered by the recipe are actually changed in current chef-client
run.
a recipe can register its dependencies as
So here the "horizon::server" is informing about its
dependencies.
This is accomplished by comparing the value of the current attributes of node
against the one already stored in the databag after the previous successful
chef-client run.
BarclampLibrary::Barclamp::DependsOn.add:
takes in a map:
This map is flushed and recreated only everyrun, however like resources
this could also be cached.
this behaviour can be altered by adding flag to config and using it
'include_recipe_smartly', however that is an extension to this behavior
and can be addressed in subsequent commits